//@author A0081229H
package storage;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;

public class Storage implements Iterable<Task>, Observer{
	private static final String ADDTASK = "addTask";
	private static final String REMOVETASK = "removeTask";
	private static final String DUPLICATETASK = "processDuplicateTask";
	private static final String UPDATE = "update";
	private static final String ENCLOSE_OPEN = "(";
	private static final String ENCLOSE_CLOSE = ")";
	
	private static TreeSet<Task> tasks;
	private static String filePath = "file.txt";
	private static Storage self = null;
	private static External externalMemory;
	private static ClashingMaintenance clashingMaintenance;

	// Constructors

	public static Storage getInstance() throws NumberFormatException, Exception {
		if (self == null) {
			self = new Storage(filePath);
			externalMemory = new External(filePath);
			externalMemory.load();
			OverdueMaintenance overdueMaintenance = new OverdueMaintenance();
			overdueMaintenance.start();
			clashingMaintenance = ClashingMaintenance.getInstance();
			return self;
		} else {
			return self;
		}
	}

	public static void setFilePath(String pFilePath) throws Exception {
		if (pFilePath == null) {
			throw new Exception(Helper.ERROR_NULL);
		}
		filePath = pFilePath;
	}

	private Storage(String pFilePath) throws NumberFormatException, Exception {
		StorageLogger.create();
		tasks = new TreeSet<Task>();
		filePath = pFilePath;
	}
	
	// Access Methods

	public ArrayList<Task> searchTask(String pDesc) {
		ArrayList<Task> results = new ArrayList<Task>();
		for (Task t : tasks) {
			String description = t.getDescription().toUpperCase();
			if (description.contains(pDesc.toUpperCase())) {
				results.add(t);
			}
		}
		return results;
	}

	public int getSize(){
		return tasks.size();
	}
	
	public TreeSet<Task> getTaskList() {
		return tasks;
	}
	
	public String getFilePath() {
		return filePath;
	}

	// Modification Methods

	public Task addTask(Task pTask) throws Exception {
		StorageLogger.log(ADDTASK + Helper.SPACE + pTask.toString());
		assert (pTask == null): Helper.ERROR_NULL;
		Task cTask = pTask;
		if (tasks.contains(pTask)) {
			cTask = processDuplicateTask(pTask);
		}
		clashingMaintenance.maintainClashesAdd(cTask, tasks);
		tasks.add(cTask);
		cTask.addObs(self);
		updateExternal();
		return cTask;
	}

	private Task processDuplicateTask(Task pTask) throws Exception {
		StorageLogger.log(DUPLICATETASK + Helper.SPACE + pTask.toString());
		assert (pTask == null): Helper.ERROR_NULL;
		Task newTask = pTask.cloneSelf();
		Task oldTask = pTask;
		for (Task t : tasks) {
			if (t.compareTo(pTask) == Helper.SAME) {
				oldTask = t;
				break;
			}
		}
		oldTask.incrementCopyCounter();
		String newDesc = newTask.getDescription() + Helper.SPACE +
				ENCLOSE_OPEN + oldTask.getCopyCounter() + ENCLOSE_CLOSE;
		newTask.setDescription(newDesc);
		return newTask;
	}

	public boolean removeTask(Task pTask) throws Exception {
		StorageLogger.log(REMOVETASK + Helper.SPACE + pTask.toString());
		assert (pTask == null): Helper.ERROR_NULL;
		boolean b = tasks.remove(pTask);
		if (b == true) {
			maintainClashes();
			updateExternal();
		}
		return b;
	}
	
	private static void maintainClashes() {
		clashingMaintenance.maintainClashes();
	}
	

	// Helper

	private void updateExternal() throws Exception {
		externalMemory.update();
	}
	
	// Interfaces

	@Override
	public Iterator<Task> iterator() {
		return tasks.iterator();
	}

	@Override
	public void update() {
		try {
			StorageLogger.log(UPDATE);
		} catch (Exception e) {
			
		}
		maintainClashes();
		try {
			updateExternal();
		} catch (Exception e) {

		}
	}
}



